package com.myssm.mybatis.org.apache.ibatis.executor;

import com.myssm.mybatis.org.apache.ibatis.configuration.BoundSql;
import com.myssm.mybatis.org.apache.ibatis.configuration.Configuration;
import com.myssm.mybatis.org.apache.ibatis.configuration.MappedStatement;
import com.myssm.mybatis.org.apache.ibatis.configuration.SqlCommandType;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.*;

public class SimpleExecutor implements Executor{

    //执行sql核心方法
    @Override
    public <E> List<E> querry(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception{
        //获取数据库连接
        DataSource dataSource = configuration.getDataSource();
        Connection connection = dataSource.getConnection();

        //解析Sql,处理占位符#{}
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        System.out.println("----SQL语句为:"+boundSql.getSqlText());

        //获取prepareStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        //todo:读取入参中@Param 根据形参绑定
        //设置参数
        String parameterType = mappedStatement.getParameterType();
        //反射获取参数类型对应的Class
        Class<?> parameterClass = getClassType(parameterType);
        //获取sql语句#{}的参数集合
        List<String> parameterMappringList = boundSql.getParameterMappringList();
        for (int i = 0; i < parameterMappringList.size(); i++) {
            String paramName = parameterMappringList.get(i);
            //暴力反射获取参数值
            if(parameterClass==Integer.class||parameterClass== Double.class){
                //当参数类型为Integer等包装类型时，把paramName设置为value,不然无法将参数值设置进去
                paramName="value";
            }
            Field declaredField = parameterClass.getDeclaredField(paramName);
            declaredField.setAccessible(true);
            //todo:策略模式处理传入多个参数
            Object data = declaredField.get(params[0]);
            System.out.println("参数【"+i+"】"+paramName+"="+data);
            //将反射获取的参数值添加到prepareStatement中
            preparedStatement.setObject(i+1,data);
        }

        //执行Sql
        String id = mappedStatement.getId();
        ResultSet resultSet=null;
        if (mappedStatement.getSqlCommandType()== SqlCommandType.DELETE||mappedStatement.getSqlCommandType()== SqlCommandType.UPDATE||mappedStatement.getSqlCommandType()== SqlCommandType.INSERT){
            //增删改
            Integer i = preparedStatement.executeUpdate();
            List<Integer> resultList=new ArrayList<>();
            resultList.add(i);
            return (List<E>) resultList;
        }else{
            //查询
            resultSet = preparedStatement.executeQuery();
        }

        //查询需要处理返回值类型
        String resultType = mappedStatement.getResultType();
        Class<?> returnClass = getClassType(resultType);
        List<Object> resultList=new ArrayList<>();
        while(resultSet.next()){
            //调用无参方法反射生成返回结果对象
            Object o = returnClass.newInstance();
            //获取resultSet一行的每列字段名
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                //字段名字
                String columnName = metaData.getColumnName(i);
                //对应的属性值
                Object value = resultSet.getObject(columnName);

                //属性封装
                //使用暴力反射反射根据数据库字段和实体类属性对应关系进行封装
                Field declaredField = returnClass.getDeclaredField(columnName);
                declaredField.setAccessible(true);
                declaredField.set(o,value);
            }

            resultList.add(o);
        }

        System.out.println("----查询结果:"+resultList);
        System.out.println("-----------------Executot执行结束--------------");
        return (List<E>) resultList;
    }



    /**
     * 完成sql语句#{}的解析
     * 1.将#{}替换为?
     * 2.将#{}里面的值进行储存
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql){
        //用于保存#{}里面的值
        List<String> parameterMappings=new ArrayList<>();

        //获取sql中”#{“和"}"的下标
        TreeMap<Integer, Integer> openCLoseIndexmap = new TreeMap<>();
        parseSql(sql,0,openCLoseIndexmap);

        //将#{}里面的值储存到parameterMappings
        Set<Map.Entry<Integer,Integer>> entries= openCLoseIndexmap.entrySet();
        for(Map.Entry<Integer,Integer> entry:entries){
            /**
             * #{param}
             *   ^    ^
             *  open close
             */
            Integer open = entry.getKey() + 2;
            Integer close=entry.getValue();
            System.out.println(open+","+close);
            parameterMappings.add(sql.substring(open,close));
        }

        //将#{}替换为?
        for(String s:parameterMappings){
            sql=sql.replace("#{"+s+"}","?");
        }

        BoundSql boundSql=new BoundSql(sql,parameterMappings);
        return boundSql;
    }

    //使用递归，将sql语句中的#{}的下标保存到openCLoseIndexmap中
    //findPosition:当前查询的下标
    //Map<Integer,Integer> openCLoseIndexmap:存放”#{“的下标和“}”的下标
    private void parseSql(String sql,int findPosition,Map<Integer,Integer> openCLoseIndexmap){

        int openIndex = sql.indexOf("#{", findPosition);
        if (openIndex!=-1){
            int endIndex=sql.indexOf("}",findPosition);
            if (endIndex!=-1){
                openCLoseIndexmap.put(openIndex,endIndex);

                findPosition=endIndex+1;
                parseSql(sql,findPosition,openCLoseIndexmap);
            }else{
                System.out.println("-----！sql语句中参数错误！-----");
            }
        }
    }

    //根据类的全名称反射获取Class
    private Class<?> getClassType(String className) throws ClassNotFoundException {
        if (className!=null){
            return Class.forName(className);
        }else return null;
    }
}
